#ifndef _Configurator_H_
#define _Configurator_H_
#include <vector>
#include <string>
#include <fstream>
#include "strKit.hpp"

//配置器
class Configurator
{
public:
    //最低日志等级 \brief
    /// 从低到高依次为：
    /// - 0 - 调试
    /// - 1 - 信息
    /// - 2 - 警告
    /// - 3 - 错误
    /// - 4 - 致命
    /// .
    int _logLevel; // 系统将记录所有不低于指定等级的日志信息
    std::string _logFile; // 日志文件路径
    int _maxJobs;// 最大抓取任务数，0表示不抓取，-1表示无限抓取
    int _maxDepth;// 最大递归深度，种子深度为0，之后逐层递增，-1表示无限深度 
    int _maxRawUrls;// 原始统一资源定位符队列最大容量，-1表示无限大    
    int _maxDnsUrls;// 解析统一资源定位符队列最大容量，-1表示无限大
    long _statInterval;// 状态间隔，即状态定时器的周期秒数，0表示不设定时器
    std::string _seeds;// 种子链接，多个链接以逗号隔开
    std::string _includePrefixes;// 包含前缀，只抓取带有这些前缀的URL，多个前缀以逗号隔开
    std::string _excludePrefixes;// 排除前缀，不抓取带有这些前缀的URL，多个前缀以逗号隔开
    std::string _pluginsPath;// 插件路径
    std::vector<std::string> _loadPlugins; // 插件列表
    std::vector<std::string> _acceptTypes; // 接受类型
public:
    //构造函数
    Configurator(void)
        : _logLevel(Debug) // 最低日志等级缺省为调试
        , _maxJobs(-1) // 最大抓取任务数缺省为无限
        ,_maxDepth(-1) // 最大递归深度缺省为无限
        ,_maxRawUrls(-1) // 原始统一资源定位符队,列最大容量缺省为无限
        ,_maxDnsUrls(-1) // 解析统一资源定位符队,列最大容量缺省为无限
        ,_statInterval(0)// 状态间隔缺省为不设定时器
    {} 

    //cfgFile配置文件路径
    void load(const std::string& cfgFile) // 从指定的配置文件中加载配置信息
    {
        // 根据路径打开配置文件输入流
        std::ifstream ifs(cfgFile.c_str());
        // 若失败
        if (!ifs)
            lg(Fatal, "ifs, %s: %d", strerror(errno), errno);
        // 记录一般错误日志
        // g_app->m_log.printf (Log::LEVEL_ERR, __FILE__, __LINE__,
        // 	"加载配置文件\"%s\"失败", cfgFile.c_str ());
        lg(Error, "bind error, %s: %d", strerror(errno), errno);

        // 文件行字符串
        std::string line;
        // 逐行读取配置文件
        for (int lineNo = 0; getline(ifs, line); ++lineNo)
        {
            // 修剪行字符串
            StrKit::trim(line);
            // 若为注释行或空行
            if (line[0] == '#' || line[0] == '\0')
                // 继续下一轮循环
                continue;

            // 拆分行字符串，以等号为分隔符，最多拆分一次
            std::vector<std::string> strv = StrKit::split(line, "=", 1);
            // 若成功拆分出键和值两个子串
            if (strv.size() == 2)
            {
                // 若键为"LOG_LEVEL"
                if (!strcasecmp(strv[0].c_str(), "LOG_LEVEL"))
                    // 则值为"最低日志等级"
                    _logLevel = atoi(strv[1].c_str());
                // 否则
                else
                    // 若键为"LOG_FILE"
                    if (!strcasecmp(strv[0].c_str(), "LOG_FILE"))
                        // 则值为"日志文件路径"
                        _logFile = strv[1];
                    // 否则
                    else
                        // 若键为"MAX_JOBS"
                        if (!strcasecmp(strv[0].c_str(), "MAX_JOBS"))
                            // 则值为"最大抓取任务数"
                            _maxJobs = atoi(strv[1].c_str());
                        // 否则
                        else
                            // 若键为"MAX_DEPTH"
                            if (!strcasecmp(strv[0].c_str(), "MAX_DEPTH"))
                                // 则值为"最大递归深度"
                                _maxDepth = atoi(strv[1].c_str());
                            // 否则
                            else
                                // 若键为"MAX_RAW_URLS"
                                if (!strcasecmp(strv[0].c_str(), "MAX_RAW_URLS"))
                                    // 则值为"原始统一资源定位符队列最大容量"
                                    _maxRawUrls = atoi(strv[1].c_str());
                                // 否则
                                else
                                    // 若键为"MAX_DNS_URLS"
                                    if (!strcasecmp(strv[0].c_str(), "MAX_DNS_URLS"))
                                        // 则值为"解析统一资源定位符队列最大容量"
                                        _maxDnsUrls = atoi(strv[1].c_str());
                                    // 否则
                                    else
                                        // 若键为"STAT_INTERVAL"
                                        if (!strcasecmp(strv[0].c_str(), "STAT_INTERVAL"))
                                            // 则值为"状态间隔"
                                            _statInterval = atoi(strv[1].c_str());
                                        // 否则
                                        else
                                            // 若键为"SEEDS"
                                            if (!strcasecmp(strv[0].c_str(), "SEEDS"))
                                                // 则值为"种子链接"
                                                _seeds = strv[1];
                                            // 否则
                                            else
                                                // 若键为"INCLUDE_PREFIXES"
                                                if (!strcasecmp(strv[0].c_str(), "INCLUDE_PREFIXES"))
                                                    // 则值为"包含前缀"
                                                    _includePrefixes = strv[1];
                                                // 否则
                                                else
                                                    // 若键为"EXCLUDE_PREFIXES"
                                                    if (!strcasecmp(strv[0].c_str(), "EXCLUDE_PREFIXES"))
                                                        // 则值为"排除前缀"
                                                        _excludePrefixes = strv[1];
                                                    // 否则
                                                    else
                                                        // 若键为"PLUGINS_PATH"
                                                        if (!strcasecmp(strv[0].c_str(), "PLUGINS_PATH"))
                                                            // 则值为"插件路径"
                                                            _pluginsPath = strv[1];
                                                        // 否则
                                                        else
                                                            // 若键为"LOAD_PLUGIN"
                                                            if (!strcasecmp(strv[0].c_str(), "LOAD_PLUGIN"))
                                                                // 则值为"插件名"
                                                                _loadPlugins.push_back(strv[1]);
                                                            // 否则
                                                            else
                                                                // 若键为"ACCEPT_TYPE"
                                                                if (!strcasecmp(strv[0].c_str(), "ACCEPT_TYPE"))
                                                                    // 则值为"接受类型"
                                                                    _acceptTypes.push_back(strv[1]);
                                                                // 否则
                                                                else
                                                                    lg(Error, "socker error, %s: %d", strerror(errno), errno);
                // 记录一般错误日志
                // g_app->m_log.printf (Log::LEVEL_ERR,  __FILE__, __LINE__,
                // 	"无效配置项: %s, %d, %s", cfgFile.c_str (),
                // 	lineNo, strv[0].c_str ());
            }
            // 否则
            else
                lg(Error, "socker error, %s: %d", strerror(errno), errno);
            // 记录一般错误日志
            // g_app->m_log.printf (Log::LEVEL_ERR,  __FILE__, __LINE__,
            // 	"无效配置行: %s, %d, %s", cfgFile.c_str (),
            // 	lineNo, line.c_str ());
        }

        // 关闭配置文件输入流
        ifs.close();
    }
};

#endif //_Configurator_H_